#pragma once

#include <io.h>
#include <stdio.h>
#include <string.h>

#pragma warning(push)
#pragma warning(disable: 4996)

namespace gtl
{

class file
{
public:
	enum seek_mode { seek_begin = SEEK_SET, seek_curr = SEEK_CUR, seek_end = SEEK_END, };

public:
	file()
	{
		m_fp = NULL;
	}

	file(FILE* fp)
	{
		m_fp = fp;
	}

	file(const char* path, const char* mode)
	{
		m_fp = NULL;
		open(path, mode);
	}

	file(const wchar_t* path, const wchar_t* mode)
	{
		m_fp = NULL;
		open(path, mode);
	}

	virtual ~file()
	{
		close();
	}

public:
	bool open(const char* path, const char* mode)
	{
		close();
		m_fp = fopen(path, mode);
		return m_fp != NULL;
	}

	bool open(const wchar_t* path, const wchar_t* mode)
	{
		close();
		m_fp = _wfopen(path, mode);
		return m_fp != NULL;
	}

	void close()
	{
		if(m_fp != NULL)
		{
			fclose(m_fp);
			m_fp = NULL;
		}
	}

	__int64 length()
	{
		if(m_fp == NULL)
			return 0;

		return _filelengthi64(m_fp->_file);
	}

	__int64 size()
	{
		return length();
	}

	__int64 tell()
	{
		if(m_fp == NULL)
			return -1;

		return _ftelli64(m_fp);
	}

	int seek(__int64 offset, int mode)
	{
		if(m_fp == NULL)
			return -1;

		return _fseeki64(m_fp, offset, mode);
	}

	template<class T>
	size_t read(T& buf)
	{
		if(m_fp == NULL)
			return 0;

		return read((unsigned char*)&buf, sizeof(T));
	}

	template<class T>
	size_t read(T* buf, size_t len)
	{
		if(m_fp == NULL)
			return 0;

		return fread(buf, sizeof(T), len, m_fp);
	}

	void read_line(char* buf, size_t len)
	{
		if(m_fp == NULL)
			return;

		fgets(buf, (int)len, m_fp);
	}

	void read_line(wchar_t* buf, size_t len)
	{
		if(m_fp == NULL)
			return;

		fgetws(buf, (int)len, m_fp);
	}

	size_t read_str(char* buf, size_t len)
	{
		if(m_fp == NULL)
			return 0;

		size_t read_size = 0;
		while(read_size < len && fgets(buf + read_size, (int)len, m_fp) != NULL)
		{
			read_size += strlen(buf + read_size);
		}

		return read_size;
	}

	size_t read_str(wchar_t* buf, size_t len)
	{
		if(m_fp == NULL)
			return 0;

		size_t read_size = 0;
		while(read_size < len && fgetws(buf + read_size, int(len - read_size), m_fp) != NULL)
		{
			read_size += wcslen(buf + read_size);
		}

		return read_size;
	}

	template<class T>
	size_t write(T& buf)
	{
		if(m_fp == NULL)
			return 0;

		return write((unsigned char*)&buf, sizeof(T));
	}

	template<class T>
	size_t write(const T* buf, size_t len)
	{
		if(m_fp == NULL)
			return 0;

		return fwrite(buf, sizeof(T), len, m_fp);
	}

	void write_str(char* buf)
	{
		if(buf == NULL || m_fp == NULL)
			return;

		fprintf(m_fp, buf);
	}

	void write_str(wchar_t* buf)
	{
		if(buf == NULL || m_fp == NULL)
			return;

		fwprintf(m_fp, buf);
	}

	bool eof()
	{
		if(m_fp == NULL)
			return true;

		return !!feof(m_fp);
	}

public:
	bool attach(FILE* fp)
	{
		close();
		m_fp = fp;
		return m_fp != NULL;
	}

	FILE* detach()
	{
		FILE* fp = m_fp;
		m_fp = NULL;
		return fp;
	}

	operator FILE*()
	{
		return m_fp;
	}

private:
	file(const file&);
	file& operator=(const file&);

protected:
	FILE* m_fp;
};

} // end of namespace gtl

#pragma warning(pop)